package checker

import (
	"sort"
	"kumachan/standalone/rpc/vd"
	"kumachan/interpreter/lang/common/name"
	"kumachan/interpreter/lang/common/source"
	"kumachan/interpreter/compiler/checker/typsys"
)

func makeVdUtilFunctionsInfo(f_reg FunctionRegistry, t_reg TypeRegistry, tim VdTypeInfoMapping) (VdUtilFunctionsInfo, source.Errors) {
	var modules = make([] string, 0)
	var modules_ = make(map[string] struct{})
	for def, _ := range tim {
		var m = def.Name.ModuleName
		var _, added = modules_[m]
		if !(added) {
			modules = append(modules, m)
			modules_[m] = struct{}{}
		}
	}
	sort.Strings(modules)
	var validator_index = make(VdValidatorIndex)
	var adapter_index = make(VdAdapterIndex)
	var err = (func() source.Errors {
	var errs source.Errors
	for _, mod := range modules {
		var validator_name = name.MakeName(mod, F_Validate)
		var adapter_name = name.MakeName(mod, F_Adapt)
		var validators = f_reg[validator_name]
		var adapters = f_reg[adapter_name]
		for _, f := range validators {
			var err = (func() *source.Error {
			var loc = f.Location
			var invalid = func() *source.Error {
				return source.MakeError(loc,
					E_InvalidValidator {}) }
			var sig = f.Signature
			if len(sig.TypeParameters) > 0 { return invalid() }
			if len(sig.ImplicitContext.Fields) > 0 { return invalid() }
			var in = sig.InputOutput.Input
			var out = sig.InputOutput.Output
			var bool_ = coreBool(t_reg)
			if !(typsys.TypeOpEqual(out, bool_)) { return invalid() }
			var def, ok = typeGetRefDef(in)
			if !(ok) { return invalid() }
			var info, exists = tim[def]
			if !(exists) { return invalid() }
			var validator_id = vd.ValidatorId(info.Id)
			var _, duplicate = validator_index[validator_id]
			if duplicate {
				return source.MakeError(loc,
					E_DuplicateValidator {}) }
			validator_index[validator_id] = f.Name
			return nil
			})()
			source.ErrorsJoin(&errs, err)
		}
		for _, f := range adapters {
			var err = (func() *source.Error {
			var loc = f.Location
			var invalid = func() *source.Error {
				return source.MakeError(loc,
					E_InvalidAdapter {}) }
			var sig = f.Signature
			if len(sig.TypeParameters) > 0 { return invalid() }
			if len(sig.ImplicitContext.Fields) > 0 { return invalid() }
			var in = sig.InputOutput.Input
			var out = sig.InputOutput.Output
			var in_def, in_ok = typeGetRefDef(in)
			if !(in_ok) { return invalid() }
			var out_def, out_ok = typeGetRefDef(out)
			if !(out_ok) { return invalid() }
			var in_info, in_exists = tim[in_def]
			if !(in_exists) { return invalid() }
			var out_info, out_exists = tim[out_def]
			if !(out_exists) { return invalid() }
			var adapter_id = vd.AdapterId {
				From: in_info.Id,
				To:   out_info.Id,
			}
			var _, duplicate = adapter_index[adapter_id]
			if duplicate {
				return source.MakeError(loc,
					E_DuplicateAdapter {}) }
			adapter_index[adapter_id] = f.Name
			return nil
			})()
			source.ErrorsJoin(&errs, err)
		}
	}
	for def, info := range tim {
		var box, is_box = def.Content.(*typsys.Box)
		if is_box && (box.BoxKind != typsys.Isomorphic) {
			// protected or opaque
			var validator_id = vd.ValidatorId(info.Id)
			var _, exists = validator_index[validator_id]
			if !(exists) {
				var err = source.MakeError(def.Location,
					E_MissingValidator {
						TypeDefName: def.Name.String(),
					})
				source.ErrorsJoin(&errs, err)
			}
		}
	}
	return errs
	})()
	if err != nil { return VdUtilFunctionsInfo {}, err }
	return VdUtilFunctionsInfo {
		VdValidatorIndex: validator_index,
		VdAdapterIndex:   adapter_index,
	}, nil
}


